﻿using DivaScript;
using KKdBaseLib;
using KKdMainLib.F2;
using MikuMikuModel.Logs;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static KKdMainLib.F2.Light;
using Type = KKdMainLib.F2.Light.Type;

namespace ft_module_parser.aftconversion.divax
{
    class pvparam_lighttest
    {
        public class PVPPTransition
        {
            public int pvid;
            public int from;
            public int to;
            public List<PvCommand> cmds = new List<PvCommand>();
            private bool processed = false;
            public PVPPTransition(int pvid, int from, int to)
            {
                this.pvid = pvid;
                this.from = from;
                this.to = to;
            }

            public void Process(List<PVPPLit> list, List<PVPPLit> list2)
            {
                if (!processed)
                {
                    Logs.WriteLine("PvParam : interpolating " + pvid + " from " + from + " to " + to, 0);
                    InterpolatePVPP(list, list2, cmds, from, to);
                    processed = true;
                }
            }
        }

        public struct PVPPFile
        {
            public int pvid;
            public int type;
            public List<PVPPLit> pVPPLit;
        }

        public struct PVPPLit
        {
            public int Id;
            public Type Type;
            public Vector3 Ambient;
            public Vector3 Diffuse;
            public Vector4 Specular;
            public Vector3 Position;
            public Vector3 ToneCurve;

            public PVPPLit(int id, Type type, Vector3 ambient, Vector3 diffuse, Vector4 specular, Vector3 position, Vector3 toneCurve)
            {
                Id = id;
                Type = type;
                Ambient = ambient;
                Diffuse = diffuse;
                Specular = specular;
                Position = position;
                ToneCurve = toneCurve;
            }

            public void Set(int id, Type type, Vector3 ambient, Vector3 diffuse, Vector4 specular, Vector3 position, Vector3 toneCurve)
            {
                Id = id;
                Type = type;
                Ambient = ambient;
                Diffuse = diffuse;
                Specular = specular;
                Position = position;
                ToneCurve = toneCurve;
            }

            public void ParseString(string[] str)
            {
                this.Set(int.Parse(str[17]), Type.PARALLEL,
                                new Vector3(float.Parse(str[1]), float.Parse(str[2]), float.Parse(str[3])),
                                new Vector3(float.Parse(str[4]), float.Parse(str[5]), float.Parse(str[6])),
                                new Vector4(float.Parse(str[7]), float.Parse(str[8]), float.Parse(str[9]), float.Parse(str[10])),
                                new Vector3(float.Parse(str[11]), float.Parse(str[12]), float.Parse(str[13])),
                                new Vector3(float.Parse(str[14]), float.Parse(str[15]), float.Parse(str[16])));
            }

            public List<string> ToString(List<string> str)
            {
                str.Add("PARALLEL" + "," + IF(Ambient.X) + "," + IF(Ambient.Y) + "," + IF(Ambient.Z)
                    + "," + IF(Diffuse.X) + "," + IF(Diffuse.Y) + "," + IF(Diffuse.Z)
                    + "," + IF(Specular.X) + "," + IF(Specular.Y) + "," + IF(Specular.Z) + IF(Specular.W)
                    + "," + IF(Position.X) + "," + IF(Position.Y) + "," + IF(Position.Z)
                    + "," + IF(ToneCurve.X) + "," + IF(ToneCurve.Y) + "," + IF(ToneCurve.Z)
                    + "," + Id);
                return str;
            }

        }
        private static string IF(float val)
        {
            return String.Format("{0:0.##}", val);
        }
        private static int I(int cur, int source, int target, int steps)
        {
            return cur + ((target - source) / steps);
        }
        private static float I(float cur, float source, float target, float steps)
        {
            return cur + ((target - source) / steps);
        }
        private static void InterpolatePVPP(List<PVPPLit> list, List<PVPPLit> list2, List<PvCommand> cmds, int source, int target)
        {
            int counter = 0;

            Logs.WriteLine("PvParam : Sanity Check " + list.Count() + "|" + list2.Count(), 2);

            var Src = list.Where(c => c.Id == source).First();
            var Tgt = list.Where(c => c.Id == target).First();
            var Src2 = list2.Where(c => c.Id == source).First();
            var Tgt2 = list2.Where(c => c.Id == target).First();
            PVPPLit Last = new PVPPLit();
            PVPPLit Last2 = new PVPPLit();
            int timestamp = 0;

            while (counter != 41)
            {
                int id = list.Max(c => c.Id) + 1;
                var src = Last;
                var src2 = Last2;
                if (counter == 0)
                {
                    src = Src;
                    src2 = Src2;
                }

                var interplit = new PVPPLit(id, Type.PARALLEL,
                        new Vector3(I(src.Ambient.X, Src.Ambient.X, Tgt.Ambient.X, 32),
                        I(src.Ambient.Y, Src.Ambient.Y, Tgt.Ambient.Y, 32), I(src.Ambient.X, Src.Ambient.X, Tgt.Ambient.X, 32)),

                        new Vector3(I(src.Diffuse.X, Src.Diffuse.X, Tgt.Diffuse.X, 32),
                        I(src.Diffuse.Y, Src.Diffuse.Y, Tgt.Diffuse.Y, 32), I(src.Diffuse.Z, src.Diffuse.Z, Tgt.Diffuse.Z, 32)),

                        new Vector4(I(src.Specular.X, Src.Specular.X, Tgt.Specular.X, 32),
                        I(src.Specular.Y, Src.Specular.Y, Tgt.Specular.Y, 32), I(src.Specular.Z, Src.Specular.Z, Tgt.Specular.Z, 32)
                        , I(src.Specular.W, Src.Specular.W, Tgt.Specular.W, 32)),

                        new Vector3(I(src.Position.X, Src.Position.X, Tgt.Position.X, 32),
                        I(src.Position.Y, Src.Position.Y, Tgt.Position.Y, 32), I(src.Position.Z, Src.Position.Z, Tgt.Position.Z, 32)),

                        new Vector3(I(src.ToneCurve.X, Src.ToneCurve.X, Tgt.ToneCurve.X, 32),
                        I(src.ToneCurve.Y, Src.ToneCurve.Y, Tgt.ToneCurve.Y, 32), I(src.ToneCurve.Z, Src.ToneCurve.Z, Tgt.ToneCurve.Z, 32)));

                var interplit2 = new PVPPLit(id, Type.PARALLEL,
                        new Vector3(I(src2.Ambient.X, Src2.Ambient.X, Tgt2.Ambient.X, 32),
                        I(src2.Ambient.Y, Src2.Ambient.Y, Tgt2.Ambient.Y, 32), I(src2.Ambient.X, Src2.Ambient.X, Tgt2.Ambient.X, 32)),

                        new Vector3(I(src2.Diffuse.X, Src2.Diffuse.X, Tgt2.Diffuse.X, 32),
                        I(src2.Diffuse.Y, Src2.Diffuse.Y, Tgt2.Diffuse.Y, 32), I(src2.Diffuse.Z, src2.Diffuse.Z, Tgt2.Diffuse.Z, 32)),

                        new Vector4(I(src2.Specular.X, Src2.Specular.X, Tgt2.Specular.X, 32),
                        I(src2.Specular.Y, Src2.Specular.Y, Tgt2.Specular.Y, 32), I(src2.Specular.Z, Src2.Specular.Z, Tgt2.Specular.Z, 32)
                        , I(src2.Specular.W, Src2.Specular.W, Tgt2.Specular.W, 32)),

                        new Vector3(I(src2.Position.X, Src2.Position.X, Tgt2.Position.X, 32),
                        I(src2.Position.Y, Src2.Position.Y, Tgt2.Position.Y, 32), I(src2.Position.Z, Src2.Position.Z, Tgt2.Position.Z, 32)),

                        new Vector3(I(src2.ToneCurve.X, Src2.ToneCurve.X, Tgt2.ToneCurve.X, 32),
                        I(src2.ToneCurve.Y, Src2.ToneCurve.Y, Tgt2.ToneCurve.Y, 32), I(src2.ToneCurve.Z, Src2.ToneCurve.Z, Tgt2.ToneCurve.Z, 32)));

                Last = interplit;
                Last2 = interplit2;
                list.Add(interplit);
                list2.Add(interplit2);
                cmds.Add(new PvCommand(timestamp, "STAGE_EFFECT", id, 0));
                timestamp = timestamp + (16 * 100);
                counter++;
            }
            cmds.Add(new PvCommand(timestamp, "STAGE_EFFECT", target, 0));
        }

        public static void PVPPProcess(string acpath, ConcurrentStack<PVPPTransition> pVPPTransitions)
        {
            Logs.WriteLine("PvParam : Processing... ", 0);
            File.WriteAllBytes("__TEMP", ft_module_parser.Properties.Resources.xpvpp);

            if (Directory.Exists(acpath + "\\mdata\\M999\\rom\\pv_param"))
                Directory.Delete(acpath + "\\mdata\\M999\\rom\\pv_param", true);

            System.IO.Compression.ZipFile.ExtractToDirectory("__TEMP", acpath + "\\mdata\\M999\\rom");

            Parallel.ForEach(Directory.EnumerateDirectories(acpath + "\\mdata\\M999\\rom\\pv_param", "*", SearchOption.AllDirectories), new ParallelOptions { MaxDegreeOfParallelism = 8 }, folder =>
            {
                var pvpptrans = new List<PVPPTransition>();
                var pvppliststage = new List<PVPPLit>();
                var pvpplistchara = new List<PVPPLit>();
                var pvcmds = new List<PvCommand>();
                int pvid = int.Parse(Path.GetFileName(folder).Remove(0, 2));

                var files = new List<string>();
                files.Add(folder + "\\stage_light.txt");
                files.Add(folder + "\\chara_light.txt");

                foreach (var file in files)
                {
                    if ((Path.GetFileNameWithoutExtension(file).Contains("stage_light")) || (Path.GetFileNameWithoutExtension(file).Contains("chara_light")))
                    {
                        string[] pvpp = null;
                        pvpp = File.ReadAllLines(file);

                        bool isDone = false;
                        StreamWriter outp = null;
                        while (!isDone)
                        {
                            try
                            {
                                outp = new StreamWriter(file, false, Encoding.ASCII);
                                isDone = true;
                            }
                            catch
                            {
                                Logs.WriteLine("PvParam : Failed! Retrying... " + Path.GetFileNameWithoutExtension(file));
                            }
                        }

                        outp.NewLine = "\n";
                        int ctr = 0;
                        foreach (var i in pvpp)
                        {
                            if (!i.Contains("Type"))
                            {
                                var u = i.Replace(",,,,,0", ",0," + ctr);
                                var str = u.Split(',');

                                if (Path.GetFileNameWithoutExtension(file).Contains("chara_light"))
                                {
                                    Logs.WriteLine("PvParam : parsing " + pvid + " chara " + u, 1);
                                    var pvparam = new PVPPLit();
                                    pvparam.ParseString(str);
                                    pvpplistchara.Add(pvparam);
                                }

                                if (Path.GetFileNameWithoutExtension(file).Contains("stage_light"))
                                {
                                    Logs.WriteLine("PvParam : parsing " + pvid + " stage " + u, 1);
                                    var pvparam = new PVPPLit();
                                    pvparam.ParseString(str);
                                    pvppliststage.Add(pvparam);
                                }

                                outp.WriteLine(u);
                                ctr++;
                            }
                            else outp.WriteLine(i);
                        }
                        outp.Close();
                    }

                    Logs.WriteLine("PvParam : Processed " + file, 1);
                }

                if (File.Exists("dsc\\common\\pv_" + pvid + "_scene.dsc"))
                {
                    var scene = PvScript.ParseBinaryScripts(new string[] { "dsc\\common\\pv_" + String.Format("{0:000}", pvid) + "_scene.dsc" }, DivaScript.Format.X, false);
                    var stage = new List<PvScript>();
                    int last_scene = -1;
                    foreach (var cmd in scene)
                    {
                        if (cmd.Opcode == "STAGE_EFFECT")
                        {
                            if (last_scene == -1)
                            {
                                last_scene = cmd.Parameters[0];
                            }
                            else if (last_scene != cmd.Parameters[0])
                            {
                                var trans = new PVPPTransition(pvid, last_scene, cmd.Parameters[0]);
                                pvpptrans.Add(trans);
                                last_scene = cmd.Parameters[0];
                            }
                        }
                    }

                    pvpptrans = pvpptrans.Distinct().ToList();

                    foreach (var i in pvpptrans)
                    {
                        i.Process(pvppliststage, pvpplistchara);
                        pVPPTransitions.Push(i);
                    }

                    var wr = new StreamWriter(folder + "\\stage_light.txt");
                    wr.WriteLine("Type,AmbientR,AmbientG,AmbientB,DiffuseR,DiffuseG,DiffuseB,SpecularR,SpecularG,SpecularB,SpecularA,PosX,PosY,PosZ,ToneCurveBegin,ToneCurveEnd,ToneCurveBlendRate,ID");
                    wr.NewLine = "\n";
                    foreach (var i in pvppliststage)
                    {
                        wr.WriteLine(i.ToString(new List<string>()).First());
                    }
                    wr.Close();

                    wr = new StreamWriter(folder + "\\chara_light.txt");
                    wr.NewLine = "\n";
                    wr.WriteLine("Type,AmbientR,AmbientG,AmbientB,DiffuseR,DiffuseG,DiffuseB,SpecularR,SpecularG,SpecularB,SpecularA,PosX,PosY,PosZ,ToneCurveBegin,ToneCurveEnd,ToneCurveBlendRate,ID");
                    foreach (var i in pvpplistchara)
                    {
                        wr.WriteLine(i.ToString(new List<string>()).First());
                    }
                    wr.Close();
                }


            });

            File.Delete("__TEMP");
        }
    }
}
